home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / utilit~1 / stelm3.lzh / STELM / ELM.C < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-11  |  19.2 KB  |  857 lines

  1. /* ELM: (c) Jos & Kees Lemmens; Oktober 1993.
  2.  
  3.    This program behaves in a simular way as the UNIX 'elm' program,
  4.    which is very popular among UNIX users. (although it has much less
  5.    functionality !)
  6.    However, the code was written completely by ourselves and we didn't
  7.    need much more !!
  8.    Unfortunately it can only handle local mail, but it is easy to add 
  9.    an external program that can handle uucp mail.
  10.  
  11.    Code runs on ATARI (under MINT), on AMIGA 500-4000 (with gcc) and
  12.    was also tested on several UNIX platforms.
  13.    
  14.    1.4 : cursor keys on Atari console now recognized;
  15.          uucp mailaddresses redirected to OutboudMail function (same
  16.          function as for Internet mail)
  17.  
  18.    Any questions or suggestions about this program can be send to:
  19.    lemmens@dv.twi.tudelft.nl
  20. */
  21.  
  22. /* standard headers */
  23.  
  24. #include <stdio.h>
  25. #include <stdlib.h>        /* getenv, malloc, free and exit */
  26. #include <string.h>
  27. #include <ctype.h>
  28. #include <time.h>
  29. #include <stdarg.h>
  30.  
  31. #ifdef __MINT__
  32. #include <osbind.h>
  33. #include <termcap.h>
  34. #include <ioctl.h>        /* ioctl & tty structs */
  35. #include <fcntl.h>        /* O_RDONLY (access) */
  36. #else
  37. #include <curses.h>
  38. #include <term.h>
  39. #include <termios.h>
  40. #include <sys/wait.h>
  41. #endif
  42.  
  43. /* UNIX headers */
  44.  
  45. #include <signal.h>        /* only for suspend routine */
  46. #include <unistd.h>
  47.  
  48. /* special headers */
  49.  
  50. #include "elm.h"
  51.  
  52. static char *version = "[ELM ST, J&KL; v1.4]";
  53.  
  54. /* termcap variables */
  55.  
  56. static char *_tcpent,*_tcpbuf;
  57. static char *CL,*CM,*SO,*SE,*CE,*ME,*MR;
  58. static int LINES,COLS;
  59.  
  60. #ifdef __MINT__
  61. struct ltchars ltold,ltnew;    /* disable suspend */
  62.  
  63. void ttraw(struct ltchars *ltold,struct ltchars *ltnew)
  64. {
  65.     if(ioctl(0, TIOCGLTC, (char *) ltold) < 0)
  66.         puts("Can't do ioctl");
  67.     ioctl(0, TIOCGLTC, (char *)ltnew);
  68.  
  69.     ltnew->t_suspc = ltnew->t_dsuspc = 0xFF;
  70.     ioctl(0, TIOCSLTC, (char *) ltnew);
  71. }
  72.  
  73. void ttcooked(struct ltchars *ltold)
  74. {
  75.     ioctl(0, TIOCSLTC, (char *) ltold);
  76. }
  77. #endif
  78.  
  79. void InitTermcap(void)
  80. {    char *tmp;
  81.  
  82.     if((tmp=getenv("TERM")) != NULL)    /* use UNIX termcap */
  83.     {    _tcpent = (char *)malloc(2048); /* must be enough ! */
  84.         tgetent(_tcpent,tmp);
  85.         _tcpbuf = _tcpent +1024;
  86.  
  87.         CL = tgetstr("cl", &_tcpbuf);    CM = tgetstr("cm", &_tcpbuf);
  88.         SO = tgetstr("so", &_tcpbuf);    SE = tgetstr("se", &_tcpbuf);
  89.         CE = tgetstr("ce", &_tcpbuf);    ME = tgetstr("me", &_tcpbuf);
  90.         MR = tgetstr("mr", &_tcpbuf);
  91.  
  92.         if((tmp=getenv("LINES")) == NULL)
  93.             LINES = tgetnum("li");
  94.         else
  95.             LINES = atoi(tmp);
  96.  
  97.         if((tmp=getenv("COLUMNS")) == NULL)
  98.             COLS = tgetnum("co");
  99.         else
  100.             COLS  = atoi(tmp);
  101.     }
  102.     else            /* set acceptable defaults : VT52 code */
  103.     {    CL = "\33H\33J";    CM = "\33Y%+ %+ ";
  104.         SO = "\33p";        SE = "\33q";
  105.         CE = "\33K";
  106.         LINES = 24;        COLS = 80;
  107.     }
  108. }
  109.  
  110. #ifdef __hpux    /* stupid error in HPUX 9.01 headerfile */
  111. int outc(char c)
  112. #else
  113. int outc(int c)
  114. #endif
  115. {    return putchar(c);
  116. }
  117.  
  118. void Clrscr(void)
  119. {    tputs(CL,1,outc);
  120. }
  121.  
  122. void Clrtoeol(void)
  123. {    tputs(CE,1,outc);
  124. }
  125.  
  126. void Gotoxy(int x,int y)
  127. {    tputs(tgoto(CM, x, y),1,outc);
  128. }
  129.  
  130. void EndTermcap(void)
  131. {    free(_tcpent);
  132. }
  133.  
  134. void PutStr (char *string,...)
  135. {    va_list ptr;
  136.  
  137.      if(string == NULL)
  138.         return;
  139.  
  140.     va_start(ptr,string);
  141.     vfprintf(stdout,string,ptr);
  142.     fflush(stdout);
  143.     va_end(ptr);
  144. }
  145.  
  146. char GetChar(void)
  147. {    char c;
  148.  
  149. #ifdef __MINT__
  150.     /* this fixes for ATARI cursor keys using scancodes */
  151.     static union { long scan; char byte[4]; } ch;
  152.  
  153.     ch.scan=Crawcin();
  154.     if (ch.byte[3] == 0)
  155.     switch(ch.byte[1])
  156.     {    case 'H': return 'k'; /* cursor up */
  157.         case 'P': return 'j'; /* cursor down */ 
  158. /*        case 'K': return 'h'; /* cursor left */
  159. /*        case 'M': return 'l'; /* cursor right */
  160.     }
  161.  
  162.     c = ch.byte[3];
  163. #else
  164. #ifdef __AMIGA__    /* doesn't echo for some reason */
  165.     read(fileno(stdin),&c,1);
  166.     write(fileno(stdout),&c,1);
  167. #else
  168.     read(fileno(stdin),&c,1);
  169. #endif
  170. #endif
  171.     return c;
  172. }
  173.  
  174. char *GetStr(char *string,int size)
  175. {
  176. #ifdef __AMIGA__
  177. /* this implies that input editing (backspacing) is not possible ! */
  178.     int count=0;
  179.     do
  180.     {    *string = GetChar();
  181.         if(*string == '\r')
  182.         {    *string = '\0';
  183.             break;
  184.         }
  185.     }    while(string++,count++ < size);
  186. #else
  187.     fgets(string,size,stdin);
  188. #endif
  189.  
  190. /* todo: try fflush(stdin) on AMIGA, maybe this works !! */
  191.  
  192.     return string;
  193. }
  194.  
  195. void usage(void)
  196. {    fputs("\nUsage: elm [-f <mailfile>] [-e <editor>]\n",stderr);
  197.     exit(1);
  198. }
  199.  
  200. void PutStatus(char *string,...)
  201. {    va_list ptr;
  202.  
  203.     va_start(ptr,string);
  204.     Gotoxy(0,LINES-2);
  205.     Clrtoeol();
  206.     vfprintf(stdout,string,ptr);
  207.     fflush(stdout);
  208.     va_end(ptr);
  209. }
  210.  
  211. void PutHeader(Mbox *M)
  212. {    Gotoxy(5,STARTHEADER);
  213.     PutStr("Mailbox '%.30s' with %d messages %s",M->Path,M->Count,version);
  214. }
  215.  
  216. void PutMenus(void)
  217. {
  218.     Gotoxy(4,STARTMENU);
  219.     PutStr("m)ail user; f)orward user; r)eply user; $ = reread mailfile; q)uit");
  220.     Gotoxy(3,STARTMENU+1);
  221.     PutStr("k = move up; j = move down; s)ave to file; d)elete mail; u)ndelete mail");
  222.     Gotoxy(4,STARTMENU+2);
  223.     PutStr("! = start shell;  To read a message, press RETURN");
  224. }
  225.  
  226. void PutMessage(Mbox *M,int x)
  227. {    int offset = 0;
  228.  
  229.     offset = M->CurMsg /(MSGLISTSIZE) * MSGLISTSIZE;
  230.  
  231.     Gotoxy(0,x-offset+STARTLIST);
  232.  
  233.     PutStr ("   %1.1s %.3d  %-6.6s  %-15.15s  (%.3d)  %-35.35s",
  234.         &M->Msglist[x]->flag,    x+1,
  235.         M->Msglist[x]->date + 4,
  236.         M->Msglist[x]->sender,
  237.         M->Msglist[x]->nrlines,
  238.         M->Msglist[x]->subject);
  239. }
  240.  
  241. void PutMessages(Mbox *M)
  242. {    int x;
  243.     int offset = 0;
  244.  
  245.     offset = M->CurMsg /(MSGLISTSIZE) * MSGLISTSIZE;
  246.  
  247.     for(x=0;x<MSGLISTSIZE && x+offset<M->Count; x++)
  248.         PutMessage(M,offset+x);
  249.  
  250.     while(x<MSGLISTSIZE)
  251.     {    Gotoxy(0,x+STARTLIST);
  252.         Clrtoeol();
  253.         x++;
  254.     }
  255. }
  256.  
  257. void OpenMailbox(Mbox *M)
  258. {    if((M->fd = fopen (ux2dos(M->Path), "r")) == NULL)
  259.     {    /* create an empty mailfile */
  260.         if((M->fd = fopen (ux2dos(M->Path), "w+")) == NULL)
  261.         {    fprintf(stderr,"Can't open mailbox for %s\n",M->User);
  262.             exit(1);
  263.         }
  264.         else
  265.         {    PutStatus("Empty mailbox %s created; Press a key ...",
  266.                 M->Path);
  267.             GetChar();
  268.         }
  269.     }
  270. }
  271.  
  272. void CloseMailbox(Mbox *M)
  273. {    fclose(M->fd);
  274. }
  275.  
  276. void RemoveCR(char *ptr)
  277. {    char *tmp;
  278.  
  279.     if((tmp=strchr(ptr,'\n')) != NULL) *tmp = 0;
  280. }
  281.  
  282. char *EatWhiteSpace(char *ptr)
  283. {    while(*ptr == ' ' || *ptr == '\t') ptr++;
  284.     return ptr;
  285. }
  286.  
  287. void BuildMailList(Mbox *M)
  288. {    int x;
  289.     int nrlines=0;
  290.     char regel[MAXSTR+1], *tmp1,*tmp2;
  291.     
  292.     OpenMailbox(M);
  293.  
  294.     for(x=0;x < MAXMSG && !feof(M->fd);x++ )
  295.     {
  296.         /* first search for start of (next) message */
  297.         while (strncmp (regel, "From ", 5) && !feof(M->fd) )
  298.         {    fgets(regel,MAXSTR,M->fd);
  299.             nrlines++;
  300.         }
  301.  
  302.         if(feof(M->fd)) break;
  303.  
  304.         M->Msglist[x] = calloc(1,sizeof(Msg));
  305.         M->Msglist[x]->start  = ftell(M->fd) - strlen(regel) - 1;
  306.         M->Msglist[x]->flag   = 'N';
  307.         M->Msglist[x]->status = 0;
  308.         /* assume new msg until proved to be old */
  309.  
  310.         if(x>0) M->Msglist[x-1]->nrlines = nrlines;
  311.  
  312.         nrlines=0;    /* start new line count */
  313.  
  314.         if((tmp1 = strchr (regel, ' ')) != NULL)    /* sender */
  315.         {    tmp1 = EatWhiteSpace(tmp1);
  316.  
  317.             if((tmp2 = strchr (tmp1, ' ')) != NULL)    /* date */
  318.             {    *tmp2++ = 0;                /* fix end of tmp1 */
  319.                 tmp2 = EatWhiteSpace(tmp2);
  320.                 strncpy(M->Msglist[x]->date,tmp2,MAXDATE);
  321.             }
  322.             strncpy(M->Msglist[x]->sender,tmp1,MAXNAME);
  323.         }
  324.         do
  325.         {    fgets (regel,MAXSTR,M->fd);
  326.             nrlines++;
  327.  
  328.             if(!strncmp (regel, "Status:",7))
  329.             {    if(strchr(regel + 7,'R') != NULL)
  330.                     M->Msglist[x]->flag = ' ';
  331.                 M->Msglist[x]->status = 1;
  332.             }
  333.             else if(!strncmp (regel, "Subject:",8))
  334.             {    tmp1 = EatWhiteSpace(regel + 8);
  335.                 RemoveCR(tmp1);
  336.                 strncpy(M->Msglist[x]->subject,tmp1,MAXSUBJECT);
  337.             }
  338.         }while (strncmp (regel, "From ", 5) && !feof(M->fd) );
  339.     }
  340.  
  341.     if(x>0) M->Msglist[x-1]->nrlines = nrlines;    /* save length for last message */
  342.  
  343.     /* last (extra) record is only used save end of file position */
  344.  
  345.     M->Msglist[x] = calloc(1,sizeof(Msg));
  346.     M->Msglist[x]->start = ftell(M->fd);
  347.  
  348.     if(x == 0)    /* fix for empty file */
  349.     {    M->Msglist[1] = calloc(1,sizeof(Msg));
  350.         M->Msglist[1]->start = ftell(M->fd);
  351.     }
  352.  
  353.     M->Count = x;
  354.     M->CurMsg = 0;
  355.     CloseMailbox(M);
  356. }
  357.  
  358. void SetCurMsg(Mbox *M,int new,int echo)
  359. {    int old;
  360.  
  361.     old = M->CurMsg;
  362.  
  363.     if(new < 0 || new >=M->Count)
  364.     {    if(echo) PutStatus("No more messages");
  365.         Gotoxy(0,STARTLIST + old%MSGLISTSIZE);    PutStr("-> ");
  366.         return;
  367.     }
  368.     M->CurMsg  = new;
  369.  
  370.     if(old /(MSGLISTSIZE) != new /(MSGLISTSIZE))
  371.         PutMessages(M);    /* show next page */
  372.  
  373.     Gotoxy(0,STARTLIST + old%MSGLISTSIZE);    PutStr("   ");
  374.     Gotoxy(0,STARTLIST + new%MSGLISTSIZE);    PutStr("-> ");
  375. }
  376.  
  377. void InitScreen(Mbox *Mailbox)
  378. {    extern void PutMenus(void);
  379.  
  380.     Clrscr();
  381.     PutHeader(Mailbox);
  382.     PutMenus();
  383.     PutMessages(Mailbox);
  384.     SetCurMsg(Mailbox,Mailbox->CurMsg,0);
  385. }
  386.  
  387. void SuspendMail(Mbox *Mailbox)
  388. {    long omask;
  389.     
  390.     if (getenv("SHLVL") != NULL) /* there is a running (C) shell */
  391.     {    omask= sigsetmask(0);
  392. #ifdef __MINT__
  393.         ttcooked(<old);
  394. #endif
  395.         (void) kill(0, SIGTSTP);
  396.         (void) sigsetmask(omask);
  397. #ifdef __MINT__
  398.         ttraw(<old,<new);
  399. #endif
  400.         InitScreen(Mailbox);
  401.     }
  402.     else
  403.         PutStatus("No shell active");
  404. }
  405.  
  406. void ListMessage(Mbox *M)
  407. {    int cn;
  408.     char ch,regel[MAXSTR+1];
  409.     Msg *Current = M->Msglist[M->CurMsg];
  410.       long start = Current->start;
  411.       long end   = M->Msglist[M->CurMsg+1]->start;
  412.  
  413.     Clrscr();
  414.  
  415.     OpenMailbox(M);
  416.     fseek(M->fd,start,SEEK_SET);
  417.     while (ftell(M->fd) < end)
  418.     {    for(cn=1;cn<LINES && ftell(M->fd) < end;cn++)
  419.         {    fgets (regel,MAXSTR,M->fd);
  420.             fputs(regel,stdout);
  421.         }
  422.         PutStatus("  Press 'i' to return to menu ...");
  423.         ch=GetChar();
  424.         if(ch == 'i') break;
  425.         Clrtoeol();
  426.     }
  427.     if(Current->flag == 'N')
  428.         Current->flag = ' ';    /* mark as read */
  429.  
  430.     CloseMailbox(M);
  431.     InitScreen(M);
  432. }
  433.  
  434. void SaveMessage(Mbox *M)
  435. {    char msgname[MAXSTR+1],regel[MAXSTR+1];
  436.     FILE *msg;
  437.       long start = M->Msglist[M->CurMsg  ]->start;
  438.       long end   = M->Msglist[M->CurMsg+1]->start;
  439.  
  440.     Gotoxy(0,STARTPROMPT + 1); Clrtoeol();
  441.     PutStr("Save as: ");
  442.     GetStr(msgname,MAXSTR);
  443.     RemoveCR(msgname);
  444.  
  445.     if((msg = fopen (ux2dos(msgname), "w")) == NULL)
  446.     {    PutStatus("Can't save mail to file");
  447.         return;
  448.     }
  449.     OpenMailbox(M);
  450.     fseek(M->fd,start,SEEK_SET);
  451.  
  452.     while (ftell(M->fd) < end)
  453.     {    fgets(regel,MAXSTR,M->fd);
  454.         fputs(regel,msg);
  455.     }
  456.  
  457.     CloseMailbox(M);
  458.     fclose(msg);
  459.     PutStatus("Mail saved to: %s",msgname);
  460. }
  461.  
  462. int StartEditor(Mbox *M,char *msgname)
  463. {    char *arg[3];
  464.     int stat=0;
  465.  
  466.     Clrscr();
  467.     arg[0]=M->Editor; arg[1]=msgname; arg[2]=NULL;
  468.  
  469.     if(access(ux2dos(arg[0]),X_OK) == 0)
  470.     {
  471. #ifdef __AMIGA__
  472.         switch(vfork())
  473. #else
  474.         switch((int)fork())
  475. #endif
  476.         {    case  0 : execv(arg[0],arg); break;
  477.             case -1 : PutStatus("Can't fork editor !"); return -1;
  478.             default : wait(&stat); break;
  479.         }
  480.     }
  481.     else
  482.     {    PutStatus("Can't open editor %s",M->Editor);
  483.         return -1;
  484.     }
  485.     return stat;
  486. }
  487.  
  488. void DeadLetter(char *msgname)
  489. {    char *dlname = DEADLETTER;
  490.     PutStatus("Can't send message, saved in %s",dlname);
  491.     remove(dlname);
  492.     rename(msgname,dlname);
  493. }
  494.  
  495. void OutboundMail(char *dest, char *subj, char *msgname)
  496. {    /* this can be replaced by an external function that
  497.        drops mail on Internet or uucp. 
  498.     */
  499.     subj=subj;    /* only to avoid compiler warnings ! */
  500.     Gotoxy(0,LINES-3);
  501.     PutStr("Only local mail: can't send to %s",dest);
  502.     DeadLetter(msgname);
  503. }
  504.     
  505. void MailMsgTo(Mbox *M,char *msgname,char *dest,char *subj)
  506. {    char destbox[MAXSTR+1];
  507.     char c=0,buffer[MAXSTR+1];
  508.     time_t t;
  509.     FILE *destfile, *msgfile;
  510.     
  511.     /* ask for confirmation or to reenter editor */
  512.  
  513.     while(c != 's')
  514.     {    Clrscr();
  515.         Gotoxy(0,STARTPROMPT);
  516.         PutStr("r)estart editor, s)end or f)orget ? ");
  517.  
  518.         switch(c=GetChar())
  519.         {    case 'f':
  520.                 PutStatus("Mail cancelled");
  521.                 InitScreen(M);
  522.                 remove(msgname);    /* delete temporary file */
  523.                 return;
  524.             case 'r':
  525.                 StartEditor(M,msgname);
  526.                 break;
  527.             default:
  528.                 break;
  529.         }
  530.     }
  531.  
  532.     InitScreen(M);
  533.  
  534.     /* catch UUCP and internet mail */
  535.     
  536.     if(strchr(dest,'@') != NULL || strchr(dest,'!') != NULL)
  537.     {    OutboundMail(dest,subj,msgname);
  538.         return;
  539.     }
  540.     
  541.     sprintf (destbox, "%s/%s", MAILDIR, dest);
  542.  
  543.     if((destfile = fopen(ux2dos(destbox),"a")) == NULL)
  544.     {    DeadLetter(msgname);
  545.         return;
  546.     }
  547.     time(&t);
  548.     
  549.     /* first line must be conform mail headers under UNIX */
  550.  
  551.     fprintf(destfile,"From %s %s",M->User,ctime(&t));
  552.     fprintf(destfile,"To: %s\n",dest);
  553.     fprintf(destfile,"Subject: %s\n",subj);
  554.     fprintf(destfile,"Mailer: %s\n\n",version);
  555.  
  556.     if((msgfile  = fopen(ux2dos(msgname),"r")) != NULL)
  557.     {    while (!feof(msgfile))
  558.         {    *buffer=0;
  559.             fgets (buffer,MAXSTR,msgfile);
  560.             fputs (buffer,destfile);
  561.         }
  562.         fclose(msgfile);
  563.         remove(msgname);    /* delete temporary file */
  564.     }
  565.     fprintf(destfile,"\n");     /* extra empty line */
  566.     fclose(destfile);    
  567.     PutStatus("Mail sent !");
  568. }
  569.  
  570. void SendMail(Mbox *M)
  571. {    char *msgname=tmpnam(NULL);
  572.     char dest[MAXSTR+1], subj[MAXSTR+1];
  573.  
  574.     Gotoxy(0,STARTPROMPT + 1); Clrtoeol();
  575.     PutStr("Mail to: ");
  576.     GetStr(dest,MAXSTR);    RemoveCR(dest);
  577.     Gotoxy(0,STARTPROMPT + 2);    Clrtoeol();
  578.     PutStr("Subject: ");
  579.     GetStr(subj,MAXSTR);    RemoveCR(subj);
  580.     
  581.     if(StartEditor(M,msgname))
  582.         return;
  583.     MailMsgTo(M,msgname,dest,subj);
  584. }
  585.  
  586. void ForwardMail(Mbox *M)
  587. {    char *msgname=tmpnam(NULL);
  588.     char regel[MAXSTR+1], dest[MAXSTR+1], subj[MAXSTR+1];
  589.     long start,end;
  590.     FILE *msg;
  591.  
  592.     Gotoxy(0,STARTPROMPT + 1);    Clrtoeol();
  593.     PutStr("Forward to: ");
  594.     GetStr(dest,MAXSTR);    RemoveCR(dest);
  595.     Gotoxy(0,STARTPROMPT + 2);    Clrtoeol();
  596.     sprintf(subj,"%s (fwd)",M->Msglist[M->CurMsg]->subject);
  597.     PutStr("Subject: %s",subj);
  598.     GetChar(); /* must be possible to edit later */
  599.  
  600.     if((msg = fopen (ux2dos(msgname), "w")) == NULL)
  601.     {    PutStatus("Can't open temporary file ");
  602.         return;
  603.     }
  604.     OpenMailbox(M);
  605.  
  606.     start = M->Msglist[M->CurMsg  ]->start;
  607.       end   = M->Msglist[M->CurMsg+1]->start;
  608.     fseek(M->fd,start,SEEK_SET);
  609.  
  610.     while (ftell(M->fd) < end)
  611.     {    fgets(regel,MAXSTR,M->fd);
  612.         fputs(PREFIX,msg);
  613.         fputs(regel,msg);
  614.     }
  615.  
  616.     CloseMailbox(M);
  617.     fclose(msg);
  618.     
  619.     Gotoxy(40,STARTPROMPT + 2);
  620.     PutStr("edit message ? (n) ");
  621.     if(GetChar() == 'y')
  622.         if(StartEditor(M,msgname))
  623.             return;
  624.  
  625.     MailMsgTo(M,msgname,dest,subj);
  626. }
  627.  
  628. void ReplyMail(Mbox *M)
  629. {    char *msgname=tmpnam(NULL);
  630.     char dest[MAXSTR+1], subj[MAXSTR+1];
  631.  
  632.     Gotoxy(0,STARTPROMPT + 1); Clrtoeol();
  633.     PutStr("Reply to: %s",M->Msglist[M->CurMsg]->sender);
  634.     strncpy(dest,M->Msglist[M->CurMsg]->sender,MAXSTR);
  635.  
  636.     Gotoxy(0,STARTPROMPT + 2); Clrtoeol();
  637.     sprintf(subj,"Re: %s",M->Msglist[M->CurMsg]->subject);
  638.     PutStr("Subject: %s",subj);
  639.     GetChar(); /* must be possible to edit later */
  640.     
  641.     if(StartEditor(M,msgname))
  642.         return;
  643.     MailMsgTo(M,msgname,dest,subj);
  644. }
  645.  
  646. void RebuildMailFile(Mbox *M)
  647. {    FILE *new;
  648.     char regel[MAXSTR+1],newname[MAXSTR+1];
  649.     char *statusline = "Status: RO\n";
  650.     long start,end;
  651.     int x,delflag=0;
  652.     
  653.     /* check if we need to update */
  654.  
  655.     for(x=0;x<M->Count;x++)
  656.         if(M->Msglist[x]->flag == 'D')
  657.             break;
  658.     if(x < M->Count)
  659.     {    Gotoxy(40,STARTPROMPT); Clrtoeol();
  660.         PutStr("Delete marked messages ? (n) ");
  661.         if(GetChar() == 'y')
  662.             delflag = 1;
  663.     }
  664.  
  665.     PutStatus("Updating mailbox %s ....",M->Path);
  666.     sprintf(newname,"%s.new",M->Path);
  667.     if((new = fopen (ux2dos(newname), "w")) == NULL)
  668.     {    PutStatus("Can't open new mailbox");
  669.         return;
  670.     }
  671.     OpenMailbox(M);
  672.  
  673.     for(x=0;x<M->Count;x++)
  674.     {    if(M->Msglist[x]->flag == 'D' && delflag == 1)
  675.             continue;    /* skip deleted messages */
  676.  
  677.         start = M->Msglist[x  ]->start;
  678.           end   = M->Msglist[x+1]->start;
  679.         fseek(M->fd,start,SEEK_SET);
  680.  
  681.         if(fgets(regel,MAXSTR,M->fd) != NULL)
  682.             fputs(regel,new);        /* Write first (From) line */
  683.  
  684.         while (ftell(M->fd) < end && !feof(M->fd))
  685.         {    fgets(regel,MAXSTR,M->fd);
  686.  
  687.             if(M->Msglist[x]->flag != 'N')
  688.             {
  689.                 /* Add a new statusline */
  690.                 if(M->Msglist[x]->status == 0)
  691.                 {    while(strchr(regel,':') != NULL &&
  692.                          ftell(M->fd) < end && !feof(M->fd))
  693.                     {    fputs(regel,new);
  694.                         fgets(regel,MAXSTR,M->fd);
  695.                     }
  696.                     fputs(statusline,new);
  697.                     M->Msglist[x]->status = 1;
  698.                 }
  699.  
  700.                 /* Or update an existing statusline */
  701.                 if(!strncmp(regel,"Status:",7))
  702.                 {    fputs(statusline,new);
  703.                     continue;
  704.                 }
  705.             }
  706.             fputs(regel,new);
  707.         }
  708.     }
  709.     
  710.     /* this is to copy new messages arrived during the session !! */
  711.  
  712.     /* goto 'official' end of file */
  713.     fseek(M->fd,M->Msglist[M->Count]->start,SEEK_SET);
  714.  
  715.     while (fgets(regel,MAXSTR,M->fd) != NULL)
  716.         fputs(regel,new);
  717.  
  718.     CloseMailbox(M);
  719.     fclose(new);
  720.     if(! remove(M->Path))
  721.         if( !rename(newname,M->Path))
  722.             return;
  723.     PutStatus("Update mailbox %s failed",M->Path);
  724. }
  725.  
  726. void HandleQuit(Mbox *M)
  727. {    Gotoxy(40,STARTPROMPT);
  728.     PutStr("Do you want to quit ? (y) ");
  729.     if(GetChar() != 'n')
  730.     {    RebuildMailFile(M);
  731.         putchar('\n');
  732.         EndTermcap();
  733. #ifdef __MINT__
  734.         ttcooked(<old);
  735. #endif
  736.         exit(0);
  737.     }
  738. }
  739.  
  740. void HandleExit(void)
  741. {    Gotoxy(40,STARTPROMPT);
  742.     PutStr("Exit without update ? (y) ");
  743.     if(GetChar() != 'n')
  744.     {    putchar('\n');
  745.         EndTermcap();
  746. #ifdef __MINT__
  747.         ttcooked(<old);
  748. #endif
  749.         exit(0);
  750.     }
  751. }
  752.  
  753. int main(int argc,char *argv[]) 
  754. {    char cmd[MAXSTR+1];
  755.     char editflag=0,fileflag=0;
  756.     Mbox Mailbox;
  757.     char *tmp;
  758.  
  759. #ifndef __MINT__
  760.     struct termios p;
  761.  
  762.     tcgetattr(fileno(stdin),&p);
  763.     p.c_lflag &= ~ICANON;
  764.     p.c_cc[VTIME] = 0;
  765.     p.c_cc[VMIN]  = 1;
  766.     tcsetattr(fileno(stdin),TCSANOW,&p);
  767. #else
  768.     ttraw(<old,<new); /* disable Ctrl-Z */
  769. #endif
  770.  
  771.     while(--argc>0)            /* parse options */
  772.     {
  773.         if(*argv[1]=='-')
  774.         {    switch(*(++argv[1]))
  775.             {    case 'f':
  776.                     strcpy(Mailbox.Path,argv[2]);
  777.                     fileflag = 1;
  778.                     break;
  779.                 case 'e':
  780.                     strcpy(Mailbox.Editor,argv[2]);
  781.                     editflag = 1;
  782.                     break;
  783.                  default :
  784.                      usage();
  785.             }
  786.         }
  787.         ++argv;
  788.     }    
  789.     InitTermcap();
  790.  
  791. /* Initialize */
  792.  
  793.     if((tmp=getenv("LOGNAME")) == NULL)
  794.         tmp="root";
  795.     strncpy(Mailbox.User,tmp,MAXNAME);
  796.  
  797.     if(!fileflag)
  798.         sprintf (Mailbox.Path, "%s/%s", MAILDIR, Mailbox.User);
  799.     if(!editflag)
  800.         sprintf (Mailbox.Editor, "%s", EDITOR);
  801.  
  802.     BuildMailList(&Mailbox);
  803.     InitScreen(&Mailbox);
  804.  
  805.     while(1)
  806.     {    Gotoxy(0,STARTPROMPT); Clrtoeol();
  807.         PutStr("Command: ");
  808.         *cmd=GetChar();
  809.         if(!isdigit(*cmd))
  810.             PutStatus("");    /* clear statusline */
  811.         switch(*cmd)
  812.         {    case'\r':
  813.             case'\n': ListMessage(&Mailbox);
  814.                       SetCurMsg(&Mailbox,Mailbox.CurMsg+1,0);
  815.                       break;
  816.             case '!': SuspendMail(&Mailbox);    /* shell */
  817.                       break;
  818.             case 'd': Mailbox.Msglist[Mailbox.CurMsg]->flag = 'D';
  819.                       PutMessage(&Mailbox,Mailbox.CurMsg);
  820.                       SetCurMsg(&Mailbox,Mailbox.CurMsg+1,0);
  821.                       break;
  822.             case 'u': Mailbox.Msglist[Mailbox.CurMsg]->flag = 'U';
  823.                       PutMessage(&Mailbox,Mailbox.CurMsg);
  824.                       SetCurMsg(&Mailbox,Mailbox.CurMsg,0);
  825.                       break;
  826.             case 'm': SendMail(&Mailbox);
  827.                       break;
  828.             case 'f': ForwardMail(&Mailbox);
  829.                       break;
  830.             case 'r': ReplyMail(&Mailbox);
  831.                       break;
  832.             case 'k': SetCurMsg(&Mailbox,Mailbox.CurMsg-1,1);
  833.                       break;
  834.             case 'j': SetCurMsg(&Mailbox,Mailbox.CurMsg+1,1);
  835.                       break;
  836.             case 's': SaveMessage(&Mailbox);
  837.                       break;
  838.             case '$': RebuildMailFile(&Mailbox);
  839.                       BuildMailList(&Mailbox);
  840.                       InitScreen(&Mailbox);
  841.                       break;
  842.             case 'q': HandleQuit(&Mailbox);
  843.                       break;
  844.             case 'x': HandleExit();
  845.                       break;
  846.             default : if(atoi(cmd) != 0)
  847.                       {    GetStr(cmd+1,MAXSTR-1);
  848.                         SetCurMsg(&Mailbox,atoi(cmd) - 1,1);
  849.                         PutStatus("Current message set to %d",
  850.                             Mailbox.CurMsg + 1);
  851.                       }
  852.                       else
  853.                           PutStatus("Unknown command !");          
  854.         }
  855.     }
  856. }
  857.